#include "keyboard.h"
#include "interrupt.h"
#include "console.h"
#include "io.h"
#include "../kernel/global.h"
#include "ioseq.h"

#define KBD_BUF_PORT 0x60

#define esc '\033'		    //esc 和 delete都没有\转义字符这种形式，用8进制代替
#define delete '\0177'
#define enter '\r'
#define tab '\t'
#define backspace '\b'

#define char_invisible 0    //功能性 不可见字符均设置为0
#define ctrl_l_char char_invisible
#define ctrl_r_char char_invisible 
#define shift_l_char char_invisible
#define shift_r_char char_invisible
#define alt_l_char char_invisible
#define alt_r_char char_invisible
#define caps_lock_char char_invisible

///定义控制字符的通码和断码
#define shift_l_make 0x2a
#define shift_r_make 0x36
#define alt_l_make 0x38
#define alt_r_make 0xe038
#define alt_r_break 0xe0b8
#define ctrl_l_make 0x1d
#define ctrl_r_make 0xe01d
#define ctrl_r_break 0xe09d
#define caps_lock_make 0x3a

//二维数组，用于记录从0x00到0x3a通码对应的按键的两种情况（如0x02，不加shift表示1，加了shift表示！）的ascii码值
//如果没有，则用ascii0替代
char keymap[][2] = {
/* 0x00 */	{0,	0},		
/* 0x01 */	{esc,	esc},		
/* 0x02 */	{'1',	'!'},		
/* 0x03 */	{'2',	'@'},		
/* 0x04 */	{'3',	'#'},		
/* 0x05 */	{'4',	'$'},		
/* 0x06 */	{'5',	'%'},		
/* 0x07 */	{'6',	'^'},		
/* 0x08 */	{'7',	'&'},		
/* 0x09 */	{'8',	'*'},		
/* 0x0A */	{'9',	'('},		
/* 0x0B */	{'0',	')'},		
/* 0x0C */	{'-',	'_'},		
/* 0x0D */	{'=',	'+'},		
/* 0x0E */	{backspace, backspace},	
/* 0x0F */	{tab,	tab},		
/* 0x10 */	{'q',	'Q'},		
/* 0x11 */	{'w',	'W'},		
/* 0x12 */	{'e',	'E'},		
/* 0x13 */	{'r',	'R'},		
/* 0x14 */	{'t',	'T'},		
/* 0x15 */	{'y',	'Y'},		
/* 0x16 */	{'u',	'U'},		
/* 0x17 */	{'i',	'I'},		
/* 0x18 */	{'o',	'O'},		
/* 0x19 */	{'p',	'P'},		
/* 0x1A */	{'[',	'{'},		
/* 0x1B */	{']',	'}'},		
/* 0x1C */	{enter,  enter},
/* 0x1D */	{ctrl_l_char, ctrl_l_char},
/* 0x1E */	{'a',	'A'},		
/* 0x1F */	{'s',	'S'},		
/* 0x20 */	{'d',	'D'},		
/* 0x21 */	{'f',	'F'},		
/* 0x22 */	{'g',	'G'},		
/* 0x23 */	{'h',	'H'},		
/* 0x24 */	{'j',	'J'},		
/* 0x25 */	{'k',	'K'},		
/* 0x26 */	{'l',	'L'},		
/* 0x27 */	{';',	':'},		
/* 0x28 */	{'\'',	'"'},		
/* 0x29 */	{'`',	'~'},		
/* 0x2A */	{shift_l_char, shift_l_char},	
/* 0x2B */	{'\\',	'|'},		
/* 0x2C */	{'z',	'Z'},		
/* 0x2D */	{'x',	'X'},		
/* 0x2E */	{'c',	'C'},		
/* 0x2F */	{'v',	'V'},		
/* 0x30 */	{'b',	'B'},		
/* 0x31 */	{'n',	'N'},		
/* 0x32 */	{'m',	'M'},		
/* 0x33 */	{',',	'<'},		
/* 0x34 */	{'.',	'>'},		
/* 0x35 */	{'/',	'?'},
/* 0x36	*/	{shift_r_char, shift_r_char},	
/* 0x37 */	{'*',	'*'},    	
/* 0x38 */	{alt_l_char, alt_l_char},
/* 0x39 */	{' ',	' '},		
/* 0x3A */	{caps_lock_char, caps_lock_char}
};

int ctrl_status = 0;        //用于记录是否按下ctrl键
int shift_status = 0;       //用于记录是否按下shift
int alt_status = 0;         //用于记录是否按下alt键
int caps_lock_status = 0;   //用于记录是否按下大写锁定
int ext_scancode = 0;       //用于记录是否是扩展码

uint32_t ext_code = 0;

struct io_seq kby_buf;

/**
 * 判断键盘传去的code是否是通码（即，按下按键所产生的编码）
*/
static bool isPressCode(uint16_t code)
{
    return !( (code & 0x0080) != 0);
}

/**
 * 判断键盘传入的code是否是扩展码,
 * 注：如果是 扩展码，那应该立即结束键盘中断处理，并在下一次进入中断是，去除下一个编码进行处理。
*/
static bool isExtCode(uint8_t code){
    return code == 0xe0;
}

/**
 * 判断一个通码是否是控制码：ctrl，shift，alt，caps等。
 * 注：这里需要传入 通码
*/
static bool isControlKey(uint16_t pressCode){
    return  (pressCode == alt_r_make) || (pressCode == ctrl_r_make) || (pressCode == alt_l_make)  || (pressCode == ctrl_l_make) 
            || (pressCode == shift_l_make)  || (pressCode == shift_r_make) 
            || (pressCode == caps_lock_make)  ;
}


static void processControKey(uint16_t pressCode, bool isPressKey)
{
  
    if(pressCode == shift_l_make){
        shift_status = isPressKey;
    }
    if(pressCode == alt_r_make){
        alt_status = isPressKey;
    }
    if(pressCode == ctrl_r_make){
        ctrl_status = isPressKey;
    }
    if(pressCode == caps_lock_make && isPressKey){
        caps_lock_status = ~caps_lock_status;
    }
}


static void processNormalKey(uint16_t pressCode, bool isPressKey)
{   

    if(isPressKey){
        uint8_t val = keymap[pressCode][(shift_status == true) || caps_lock_status];
        io_seq_add(&kby_buf,val);
        //console_put_char();
    }
}

static void keyboard_handler(void)
{
    uint16_t code = inb(KBD_BUF_PORT);
    if( isExtCode(code)){
        ext_code = 1;
        return;
    }

    if(ext_code){
        code = ( (0xe000) | (code) );    //合并扫描码，这样两字节的按键的扫描码就得到了完整取出
        ext_code = 0;
    }


    bool isPressKey = false;
    if(isPressCode(code)){
        isPressKey = true;
    }else{
        code &= 0xff7f;  //将扫描码(现在是断码)还原成通码
    }
    //下面全部使用通码 + 通码/断码状态 两个字段进行逻辑处理
    if(isControlKey(code)){
        processControKey(code,isPressKey);
    }else{
        processNormalKey(code,isPressKey);
    }
 
    
}

uint8_t kbd_get(void){
    return io_seq_get(&kby_buf);
}
void keyboard_init(void)
{
    put_str("keyboard init start\n");
    io_seq_init(&kby_buf);
    register_handler(0x21, keyboard_handler);
    put_str("keyboard init done\n");
}